﻿using Kingdee.BOS.App;
using Kingdee.BOS.App.Data;
using Kingdee.BOS.Contracts;
using Kingdee.BOS.Core.Bill;
using Kingdee.BOS.Core.DynamicForm;
using Kingdee.BOS.Core.DynamicForm.Operation;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
using Kingdee.BOS.Core.Interaction;
using Kingdee.BOS.Core.List;
using Kingdee.BOS.Core.List.PlugIn;
using Kingdee.BOS.Core.Metadata;
using Kingdee.BOS.Core.Metadata.ConvertElement;
using Kingdee.BOS.Core.Metadata.ConvertElement.ServiceArgs;
using Kingdee.BOS.Orm;
using Kingdee.BOS.Orm.DataEntity;
using Kingdee.BOS.ServiceHelper;
using Kingdee.BOS.Util;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace K.KDL
{
    public class CustomArListPlugin : AbstractListPlugIn
    {
        private FormMetadata _arMeta;

        const string OSINFO_SQL = @"/*dialect*/SELECT OS.FBILLNO, OS.FID, OS.FDOCUMENTSTATUS, OSE.FSEQ, OSE.FENTRYID, M.FNUMBER AS MAT_NUMBER, OSE.FREALQTY
                    FROM T_SAL_OUTSTOCK OS
                    INNER JOIN T_SAL_OUTSTOCKENTRY OSE ON OS.FID=OSE.FID
                    INNER JOIN T_BD_MATERIAL M ON OSE.FMATERIALID=M.FMATERIALID
                    WHERE OS.FBILLNO IN ({0})";

        const string RSINFO_SQL = @"/*dialect*/SELECT RS.FBILLNO, RS.FID, RS.FDOCUMENTSTATUS, RSE.FSEQ, RSE.FENTRYID, M.FNUMBER AS MAT_NUMBER, RSE.FREALQTY
                    FROM T_SAL_RETURNSTOCK RS
                    INNER JOIN T_SAL_RETURNSTOCKENTRY RSE ON RS.FID=RSE.FID
                    INNER JOIN T_BD_MATERIAL M ON RSE.FMATERIALID=M.FMATERIALID
                    WHERE RS.FBILLNO IN ({0})";

        public override void BarItemClick(BarItemClickEventArgs e)
        {
            base.BarItemClick(e);

            if (e.BarItemKey.Equals("btnImportAr", StringComparison.OrdinalIgnoreCase))
            {
                ImportAr();
            }
        }

        private void ImportAr()
        {
            DynamicFormShowParameter showParam = new DynamicFormShowParameter();
            showParam.FormId = "SHKD_EXCELIMPORT";
            showParam.Caption = "关联应收单导入";
            this.View.ShowForm(showParam, _ =>
            {
                if (_.ReturnData != null)
                {
                    var entities = InitialArExcelInfo(_.ReturnData as DataSet);
                    if (entities == null || entities.Count == 0) return;

                    var operateResults = new OperateResultCollection();
                    var successCount = ImportAr(entities, operateResults);

                    operateResults.Add
                        (
                            new OperateResult
                            {
                                Message = $"共选择{entities.Count}条记录，成功导入{successCount}条应收记录",
                                MessageType = MessageType.Normal,
                                Name = "汇总",
                                SuccessStatus = true,
                            }
                        );
                    this.View.Refresh();
                    this.View.ShowOperateResult(operateResults);
                }
            });
        }

        private int ImportAr(List<ArExcelInfo> entities, OperateResultCollection operateResults)
        {
            var successCount = 0;

            if (_arMeta == null)
            {
                _arMeta = FormMetaDataCache.GetCachedFormMetaData(Context, "AR_receivable");
            }

            var viewService = ServiceHelper.GetService<IViewService>();
            var view = BosHelper.CreateBillView(Context, "AR_receivable");
            foreach (var lineGroup in entities.GroupBy(_ => new { _.SrcFormType, _.ArSeq }))
            {
                try
                {
                    var bill = Convert2AR(lineGroup.ToList(), view);
                    OperateOption option = OperateOption.Create();
                    option.SetIgnoreWarning(true);
                    option.SetIgnoreInteractionFlag(true);
                    var operationResult = BusinessDataServiceHelper.Save(Context, _arMeta.BusinessInfo, bill, option, "Save");
                    if (!operationResult.IsSuccess)
                    {
                        var validationMessage = new StringBuilder();
                        foreach (var validation in operationResult.ValidationErrors)
                        {
                            validationMessage.AppendLine(validation.Message);
                        }

                        operateResults.Add
                        (
                            new OperateResult
                            {
                                Message = $"下推保存时验证失败，具体信息如下：{validationMessage}",
                                MessageType = MessageType.Warning,
                                Name = $"导入：应收序号[{lineGroup.Key.ArSeq}], 源单类型=[{lineGroup.Key.SrcFormType}]",
                                SuccessStatus = false,
                            }
                        );
                    }
                    else
                    {
                        successCount += lineGroup.Count();
                    }
                }
                catch (Exception ex)
                {
                    operateResults.Add
                        (
                            new OperateResult
                            {
                                Message = new StringBuilder(ex.Message).AppendLine(ex.StackTrace).ToString(),
                                MessageType = MessageType.Warning,
                                Name = $"导入：应收序号[{lineGroup.Key.ArSeq}], 源单类型=[{lineGroup.Key.SrcFormType}]",
                                SuccessStatus = false,
                            }
                        );
                }
            }
            view.Close();

            return successCount;
        }

        DynamicObject[] Convert2AR(IEnumerable<ArExcelInfo> lines, IBillView view)
        {
            var _1stLine = lines.First();

            ConvertRuleElement rule;
            var isOS = _1stLine.SrcFormType == "销售出库单";
            var srcFormID = isOS ? "SAL_OUTSTOCK" : "SAL_RETURNSTOCK";
            if (isOS)
            {
                List<ConvertRuleElement> rules = ConvertServiceHelper.GetConvertRules(Context, srcFormID, "AR_receivable");
                //rule = rules.FirstOrDefault(_ => _.IsDefault);
                //rule = rules.FirstOrDefault(_ => _.Id == "5ca3cadf-a272-4fe4-8f36-9a49f9e7c7dd");
                rule = rules.FirstOrDefault(_ => _.Id == "AR_OutStockToReceivableMap");
            }
            else if (!isOS)
            {
                List<ConvertRuleElement> rules = ConvertServiceHelper.GetConvertRules(Context, srcFormID, "AR_receivable");
                //rule = rules.FirstOrDefault(_ => _.IsDefault);
                rule = rules.FirstOrDefault(_ => _.Id == "AR_ReturnToReceivableMap");
            }
            else
            {
                throw new Exception("非预期中的源单类型");
            }

            var rows = DBUtils.ExecuteDynamicObject(Context, string.Format(isOS ? OSINFO_SQL : RSINFO_SQL, string.Join(",", lines.Select(_ => $"'{_.SrcBillNo}'").Distinct())));
            if (rows == null || rows.Count == 0)
            {
                throw new InvalidDataException($"系统中找不到应收序号为[{_1stLine.ArSeq}]的[{_1stLine.SrcFormType}]单据[{string.Join(",", lines.Select(_ => _.SrcBillNo).Distinct())}]");
            }

            var selectedRows = new List<ListSelectedRow>();
            foreach (var line in lines)
            {
                var matchedLines = rows.Where(_ => _["FBILLNO"].ToString() == line.SrcBillNo && (int)_["FSEQ"] == line.LineNo && _["MAT_NUMBER"].ToString() == line.MaterialNo);
                if (matchedLines == null)
                {
                    throw new InvalidOperationException($"单据中无法匹配到序号为[{line.LineNo}]且物料编码为[{line.MaterialNo}]的行");
                }
                if (matchedLines.Count() > 1)
                {
                    throw new InvalidOperationException($"单据中匹配到序号为[{line.LineNo}]且物料编码为[{line.MaterialNo}]的行超过一行");
                }
                var matchedLine = matchedLines.First();
                var row = new ListSelectedRow(matchedLine["FID"].ToString(), matchedLine["FENTRYID"].ToString(), line.LineNo, srcFormID);
                row.EntryEntityKey = "FEntity";
                selectedRows.Add(row);
            }

            ConvertOperationResult operationResult = null;
            //var custParams = new Dictionary<string, object>();
            //custParams.Add("CustLines", entries);
            var pushArgs = new PushArgs(rule, selectedRows.ToArray())
            {
                TargetOrgId = 0,
                TargetBillTypeId = "180ecd4afd5d44b5be78a6efe4a7e041",
                //CustomParams = custParams,
            };
            operationResult = ConvertServiceHelper.Push(Context, pushArgs, OperateOption.Create());
            DynamicObject[] objs = (from p in operationResult.TargetDataEntities
                                    select p.DataEntity).ToArray();

            var info = _arMeta.BusinessInfo;
            foreach (var toSaveBill in objs)
            {
                view.CreateNewModelData();
                view.Model.DataObject = toSaveBill;
                view.UpdateView();

                var entry = toSaveBill["AP_PAYABLEENTRY"] as DynamicObjectCollection;
                for (int i = 0; i < entry.Count; i++)
                {
                    var line = entry[i];
                    var linkLines = line["FEntityDetail_Link"] as DynamicObjectCollection;
                    if (linkLines == null || linkLines.Count == 0) continue;
                    var linkLine = linkLines[0];
                    //custData.Where(_ => _.SoEntryId.ToString() == link["SId"].ToString()).ToList();
                    var matchRow = rows.SingleOrDefault(_ => _["FENTRYID"].ToString() == linkLine["SId"].ToString());
                    if (matchRow != null)
                    {
                        var srcLine = lines.Where(_ => matchRow["FBILLNO"].ToString() == _.SrcBillNo && (int)matchRow["FSEQ"] == _.LineNo && matchRow["MAT_NUMBER"].ToString() == _.MaterialNo).First();
                        view.Model.SetValue("F_PATC_KS", srcLine.KS, i);
                        view.Model.SetValue("FComment", srcLine.BZ, i);
                        view.Model.SetValue("FTaxRate", srcLine.TaxRate, i);
                        view.InvokeFieldUpdateService("FTaxRate", i);
                        view.Model.SetValue("FTaxPrice", srcLine.TaxPrice, i);
                        view.InvokeFieldUpdateService("FTaxPrice", i);
                        view.Model.SetValue("FPriceQty", isOS ? srcLine.Qty : -srcLine.Qty, i);
                        view.InvokeFieldUpdateService("FPriceQty", i);
                    }
                }
            }

            return objs;
        }

        private List<ArExcelInfo> InitialArExcelInfo(DataSet ds)
        {
            var result = new List<ArExcelInfo>();
            if (ds == null || ds.Tables.Count == 0) return result;

            return ds.Tables[0].Rows
                .Cast<DataRow>()
                .Select(_ => new ArExcelInfo
                {
                    ArSeq = _["应收单序号"].GetStringValue(),
                    SrcFormType = _["源单类型"].GetStringValue(),
                    SrcBillNo = _["源单号"].GetStringValue(),
                    LineNo = _["行号"].GetIntValue(),
                    MaterialNo = _["物料编码"].GetStringValue(),
                    MaterialName = _["物料名称"].GetStringValue(),
                    Qty = _["计价数量"].GetDecimalValue(),
                    TaxPrice = _["含税单价"].GetDecimalValue(),
                    TaxRate = _["税率"].GetDecimalValue(),
                    KS = _["科室"].GetStringValue(),
                    BZ = _["备注"].GetStringValue(),

                }).ToList();
        }
    }

    public class ArExcelInfo
    {
        public string ArSeq { get; set; }
        public string SrcFormType { get; internal set; }
        public string SrcBillNo { get; internal set; }
        public int LineNo { get; internal set; }
        public string MaterialNo { get; internal set; }
        public string MaterialName { get; internal set; }
        public decimal Qty { get; internal set; }
        public decimal TaxPrice { get; internal set; }
        public decimal TaxRate { get; internal set; }
        public string KS { get; set; }
        public string BZ { get; set; }
    }

    public class PushLineInfo
    {

    }
}

